/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package control;

import entidad.Medicion;
import entidad.Persistencia;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;

/**
 * Clase que contiene la logica de las operaciones de Regresion/Interpolación y
 * Metodos estadisticos de Dispersión y de Tendencia
 *
 * @author Zergio
 */
public class ComponenteMatematico extends MedicionesEstadisticas{
    
    private static ComponenteMatematico m_instance;

    private ComponenteMatematico() {
        //constructor
    }
    
    public synchronized static ComponenteMatematico getInstance() {
        if (m_instance == null) {
            m_instance = new ComponenteMatematico();
        }

        return m_instance;
    }
    
    

    //(8) Interpolacion Lagrange
    public ArrayList<Medicion> MetodoInterpolacion(ArrayList<Medicion> mediciones, int numeroPredicciones) {

        ArrayList<Medicion> prediccion = (ArrayList<Medicion>) mediciones.clone();
        ArrayList<Double> tiempos = new ArrayList<Double>();
        ArrayList<Double> imagenesT = new ArrayList<Double>();
        ArrayList<Double> imagenesP = new ArrayList<Double>();
        ArrayList<Double> imagenesA = new ArrayList<Double>();

        for (Medicion m : prediccion) {
            tiempos.add((double) prediccion.indexOf(m));
            imagenesT.add(m.getTemperatura());
            imagenesP.add(m.getPluviosidad());
            imagenesA.add(m.getPresionAtmosferica());
        }

        /**
         * Tiempos hace referencia a los momentos t medidos Imagenes hace
         * referencia a los valores de esas prediccion
         *
         * Se hace una interpolación de valores para minimo dos puntos de data y
         * maximo 4(las ultimas 4)
         *
         * retorna falso si hay pocos datos (util para mostrar errores)
         */
        int n;
        double x, t, p, a;
        double matrizT[][] = new double[4][2];
        double matrizP[][] = new double[4][2];
        double matrizA[][] = new double[4][2];

        System.out.println(imagenesT.size() + "\n");

        if (tiempos.size() < 2) {
            return null;
        }
        if (tiempos.size() > 4) {
            do {
                System.out.println("1");
                tiempos.remove(1);
                imagenesT.remove(1);
                imagenesP.remove(1);
                imagenesA.remove(1);
            } while (tiempos.size() > 4);

        }


        n = tiempos.size();
        System.out.println(n + "\n");

        for (int j = 1; j <= n; j++) {

            matrizT[j - 1][0] = tiempos.get(j - 1);
            matrizP[j - 1][0] = tiempos.get(j - 1);
            matrizA[j - 1][0] = tiempos.get(j - 1);

            matrizT[j - 1][1] = imagenesT.get(j - 1);
            matrizP[j - 1][1] = imagenesP.get(j - 1);
            matrizA[j - 1][1] = imagenesA.get(j - 1);
        }

        double valorTope = tiempos.get(tiempos.size() - 1);

        for (int i = 1; i <= numeroPredicciones; i++) {
            x = valorTope + i;
            t = 0;
            p = 0;
            a = 0;

            switch (n) {
                case 2:
                    tiempos.add(x);
                    t = (((((x - matrizT[1][0]) * matrizT[0][1]) / (matrizT[0][0] - matrizT[1][0])) + (((x - matrizT[0][0]) * matrizT[1][1]) / (matrizT[1][0] - matrizT[0][0]))));
                    imagenesT.add(t);
                    p = (((((x - matrizP[1][0]) * matrizP[0][1]) / (matrizP[0][0] - matrizP[1][0])) + (((x - matrizP[0][0]) * matrizP[1][1]) / (matrizP[1][0] - matrizP[0][0]))));
                    imagenesP.add(p);
                    a = (((((x - matrizA[1][0]) * matrizA[0][1]) / (matrizA[0][0] - matrizA[1][0])) + (((x - matrizA[0][0]) * matrizA[1][1]) / (matrizA[1][0] - matrizA[0][0]))));
                    imagenesA.add(a);


                    break;
                case 3:
                    tiempos.add(x);
                    t = ((x - matrizT[1][0]) * (x - matrizT[2][0]) * matrizT[0][1]) / ((matrizT[0][0] - matrizT[1][0]) * (matrizT[0][0] - matrizT[2][0])) + ((x - matrizT[0][0]) * (x - matrizT[2][0]) * matrizT[1][1]) / ((matrizT[1][0] - matrizT[0][0]) * (matrizT[1][0] - matrizT[2][0])) + ((x - matrizT[1][0]) * ((x - matrizT[0][0]) * matrizT[2][1]) / ((matrizT[2][0] - matrizT[0][0]) * (matrizT[2][0] - matrizT[1][0])));
                    imagenesT.add(t);
                    p = ((x - matrizP[1][0]) * (x - matrizP[2][0]) * matrizP[0][1]) / ((matrizP[0][0] - matrizP[1][0]) * (matrizP[0][0] - matrizP[2][0])) + ((x - matrizP[0][0]) * (x - matrizP[2][0]) * matrizP[1][1]) / ((matrizP[1][0] - matrizP[0][0]) * (matrizP[1][0] - matrizP[2][0])) + ((x - matrizP[1][0]) * ((x - matrizP[0][0]) * matrizP[2][1]) / ((matrizP[2][0] - matrizP[0][0]) * (matrizP[2][0] - matrizP[1][0])));
                    imagenesP.add(p);
                    a = ((x - matrizA[1][0]) * (x - matrizA[2][0]) * matrizA[0][1]) / ((matrizA[0][0] - matrizA[1][0]) * (matrizA[0][0] - matrizA[2][0])) + ((x - matrizA[0][0]) * (x - matrizA[2][0]) * matrizA[1][1]) / ((matrizA[1][0] - matrizA[0][0]) * (matrizA[1][0] - matrizA[2][0])) + ((x - matrizA[1][0]) * ((x - matrizA[0][0]) * matrizA[2][1]) / ((matrizA[2][0] - matrizA[0][0]) * (matrizA[2][0] - matrizA[1][0])));
                    imagenesA.add(a);
                    break;
                case 4:
                    tiempos.add(x);
                    t = ((x - matrizT[1][0]) * (x - matrizT[3][0]) * (x - matrizT[2][0]) * matrizT[0][1]) / ((matrizT[0][0] - matrizT[1][0]) * (matrizT[0][0] - matrizT[2][0]) * (matrizT[0][0] - matrizT[3][0])) + ((x - matrizT[0][0]) * (x - matrizT[2][0]) * (x - matrizT[3][0]) * matrizT[1][1]) / ((matrizT[1][0] - matrizT[0][0]) * (matrizT[1][0] - matrizT[2][0]) * (matrizT[1][0] - matrizT[3][0])) + ((x - matrizT[0][0]) * (x - matrizT[1][0]) * ((x - matrizT[3][0]) * matrizT[2][1]) / ((matrizT[2][0] - matrizT[0][0]) * (matrizT[2][0] - matrizT[1][0]) * (matrizT[2][0] - matrizT[3][0])) + ((x - matrizT[0][0]) * (x - matrizT[1][0]) * ((x - matrizT[2][0]) * matrizT[
3][1]) / ((matrizT[3][0] - matrizT[0][0]) * (matrizT[3][0] - matrizT[1][0]) * (matrizT[3][0] - matrizT[2][0]))));
                    imagenesT.add(t);
                    p = ((x - matrizP[1][0]) * (x - matrizP[3][0]) * (x - matrizP[2][0]) * matrizP[0][1]) / ((matrizP[0][0] - matrizP[1][0]) * (matrizP[0][0] - matrizP[2][0]) * (matrizP[0][0] - matrizP[3][0])) + ((x - matrizP[0][0]) * (x - matrizP[2][0]) * (x - matrizP[3][0]) * matrizP[1][1]) / ((matrizP[1][0] - matrizP[0][0]) * (matrizP[1][0] - matrizP[2][0]) * (matrizP[1][0] - matrizP[3][0])) + ((x - matrizP[0][0]) * (x - matrizP[1][0]) * ((x - matrizP[3][0]) * matrizP[2][1]) / ((matrizP[2][0] - matrizP[0][0]) * (matrizP[2][0] - matrizP[1][0]) * (matrizP[2][0] - matrizP[3][0])) + ((x - matrizP[0][0]) * (x - matrizP[1][0]) * ((x - matrizP[2][0]) * matrizP[
3][1]) / ((matrizP[3][0] - matrizP[0][0]) * (matrizP[3][0] - matrizP[1][0]) * (matrizP[3][0] - matrizP[2][0]))));
                    imagenesP.add(p);
                    a = ((x - matrizA[1][0]) * (x - matrizA[3][0]) * (x - matrizA[2][0]) * matrizA[0][1]) / ((matrizA[0][0] - matrizA[1][0]) * (matrizA[0][0] - matrizA[2][0]) * (matrizA[0][0] - matrizA[3][0])) + ((x - matrizA[0][0]) * (x - matrizA[2][0]) * (x - matrizA[3][0]) * matrizA[1][1]) / ((matrizA[1][0] - matrizA[0][0]) * (matrizA[1][0] - matrizA[2][0]) * (matrizA[1][0] - matrizA[3][0])) + ((x - matrizA[0][0]) * (x - matrizA[1][0]) * ((x - matrizA[3][0]) * matrizA[2][1]) / ((matrizA[2][0] - matrizA[0][0]) * (matrizA[2][0] - matrizA[1][0]) * (matrizA[2][0] - matrizA[3][0])) + ((x - matrizA[0][0]) * (x - matrizA[1][0]) * ((x - matrizA[2][0]) * matrizA[
3][1]) / ((matrizA[3][0] - matrizA[0][0]) * (matrizA[3][0] - matrizA[1][0]) * (matrizA[3][0] - matrizA[2][0]))));
                    imagenesA.add(a);

                    break;
                default:
                    return null;

//TODO Cambiar impresiones de Linea por Appends a Listas Ingresadas
            }
            Calendar cal = Calendar.getInstance();
            cal.setTime(prediccion.get(prediccion.size() - 1).getFecha());
            cal.add(Calendar.DATE, +1);
            Medicion m = new Medicion(cal.getTime(), t, p, a);
            prediccion.add(m);

        }
        return prediccion;
    }

    public double Moda(ArrayList<Double> datos) {
        Collections.sort(datos);
        int cuentaMaxima = 0;
        double valorMaximo = 0;

        for (int i = 0; i < datos.size(); ++i) {
            int temp = 0;
            for (int j = 0; j < datos.size(); ++j) {
                if (datos.get(j).doubleValue() == datos.get(i).doubleValue()) {
                    ++temp;
                }
            }
            if (temp > cuentaMaxima) {
                cuentaMaxima = temp;
                valorMaximo = datos.get(i);
            }
        }

        return valorMaximo;
    }    
    
    public void calcularEstadisticas() {
        Persistencia datos = Persistencia.getInstance();
        MedicionesEstadisticas control = ComponenteMatematico.getInstance();

        if (datos.getHistorico().size() >= 3 ) {
            ArrayList<Double> temp = new ArrayList<Double>();
            ArrayList<Double> pluv = new ArrayList<Double>();
            ArrayList<Double> pres = new ArrayList<Double>();
            
            for (Medicion m : datos.getHistorico()) {
                temp.add(m.getTemperatura());
                pluv.add(m.getPluviosidad());
                pres.add(m.getPresionAtmosferica());
            }
            
            control.setDatos(temp);
            
            if (!temp.isEmpty()) {
                datos.getTemperatura().setModa(((ComponenteMatematico)control).Moda(temp));
                control = new Media(control);
                datos.getTemperatura().setMedia(control.Calcular());
                control = new Varianza(control);
                datos.getTemperatura().setVarianza(control.Calcular());
                control = new Desviacion(control);
                datos.getTemperatura().setDesviacion(control.Calcular());                
                control = new Mediana(control);
                datos.getTemperatura().setMediana(control.Calcular());                
                control = new RangoIntercuartilico(control);
                datos.getTemperatura().setRango(control.Calcular());                
            }
            control = ComponenteMatematico.getInstance();
            control.setDatos(pluv);
            if (!pluv.isEmpty()) {
                datos.getPluviosidad().setModa(((ComponenteMatematico) control).Moda(pluv));
                control = new Media(control);
                datos.getPluviosidad().setMedia(control.Calcular());
                control = new Varianza(control);
                datos.getPluviosidad().setVarianza(control.Calcular());
                control = new Desviacion(control);
                datos.getPluviosidad().setDesviacion(control.Calcular());                
                control = new Mediana(control);
                datos.getPluviosidad().setMediana(control.Calcular());                
                control = new RangoIntercuartilico(control);
                datos.getPluviosidad().setRango(control.Calcular());
                
            }
            control = ComponenteMatematico.getInstance();
            control.setDatos(pres);
            if (!pres.isEmpty()) {
                datos.getPresionAtmosferica().setModa(((ComponenteMatematico) control).Moda(pres));
                control = new Media(control);
                datos.getPresionAtmosferica().setMedia(control.Calcular());
                control = new Varianza(control);
                datos.getPresionAtmosferica().setVarianza(control.Calcular());
                control = new Desviacion(control);
                datos.getPresionAtmosferica().setDesviacion(control.Calcular());                
                control = new Mediana(control);
                datos.getPresionAtmosferica().setMediana(control.Calcular());                
                control = new RangoIntercuartilico(control);
                datos.getPresionAtmosferica().setRango(control.Calcular());
            }
        }
    }

    @Override
    public double Calcular() {
        return 0;
    }
}
